﻿package render3d.adapters 
{
	import com.paultondeur.pv3d.objects.Sprite3D;
	import flash.display.BitmapData;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import org.papervision3d.cameras.Camera3D;
	import org.papervision3d.core.proto.MaterialObject3D;
	import org.papervision3d.materials.BitmapMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.render.BasicRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.papervision3d.view.Viewport3D;
	import render3d.adapters.abstract.Render3DAdapterAbstract;
	import render3d.CrossEngineObject3D;
	import render3d.interfaces.IRender3DAdapter;
	import render3d.interfaces.IRender3DForObject3D;
	/**
	 * Papervision3D 2.1 Adapter
	 * @author Exey Panteleev
	 */
	public class Papervision3DAdapter extends Render3DAdapterAbstract implements IRender3DAdapter, IRender3DForObject3D
	{
		private var viewport:Viewport3D;
		private var renderer:BasicRenderEngine;
		private var scene:Scene3D;	
		private var camera:Camera3D;
		
		public function Papervision3DAdapter() 
		{
			initScene();
		}
		
		public function initScene():void 
		{
			scene = new Scene3D()
			camera = new Camera3D(60, 0, 5000, true, false);
			viewport = new Viewport3D(800, 600, true, false, true, false);
			renderer = new BasicRenderEngine();
			viewContainer.addChild( viewport );
		}
		
		//--------------------------------------------------------------------------
		//
		//  Camera
		//
		//--------------------------------------------------------------------------
		
		public function setCameraPosition(x:Number = NaN, y:Number = NaN, z:Number = NaN):void 
		{
			if (!isNaN(x)) camera.x = x;
			if (!isNaN(y)) camera.y = y;
			if (!isNaN(z)) camera.z = z;
		}
		
		public function moveCamera(forwardSpeed:Number, rightSpeed:Number, jumpSpeed:Number):void 
		{
			camera.x += rightSpeed;
			camera.y -= forwardSpeed;
			camera.z += jumpSpeed;
		}
		
		public function rotateCamera(x:Number = NaN, y:Number = NaN, z:Number = NaN):void 
		{
			if (!isNaN(x)) camera.rotationX = -x;
			if (!isNaN(y)) camera.rotationY = y;
			if (!isNaN(z)) camera.rotationZ = z;
		}
		
		public function lookCamera(mouseX:Number, mouseY:Number):void {
			camera.rotationX += mouseY;
			camera.rotationZ += mouseX;
		}
		
		public function getCameraTransformation():Matrix3D 
		{
			var matrix:Matrix3D = new Matrix3D();
			var components:Vector.<Vector3D> = new Vector.<Vector3D>();
			components[0] = new Vector3D(camera.x, camera.y, camera.z);
			components[1] = new Vector3D(camera.rotationX * toRADIANS, camera.rotationY * toRADIANS, camera.rotationZ * toRADIANS);
			components[2] = new Vector3D(1, 1, 1);
			matrix.recompose(components);
			return matrix;
		}

		public function getCameraHeight():Number 
		{
			return camera.z;
		}
		
		public function getCameraPanAngle():Number 
		{
			return -camera.rotationZ*toRADIANS;
		}
		
		//--------------------------------------------------------------------------
		//
		//  3D Objects 
		//
		//--------------------------------------------------------------------------
		
		public function createPlane(bitmapData:BitmapData, width:int, height:int, type:String = ""):CrossEngineObject3D
		{
			var material:BitmapMaterial = new BitmapMaterial(bitmapData);
			var plane:Plane = new Plane( material, width, height, 4, 4 );
			plane.rotationX = 180;
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, plane);
			return crossEngineObject3D;				
		}		
		
		public function createSkybox(backTexture:BitmapData, leftTexture:BitmapData, frontTexture:BitmapData, rightTexture:BitmapData, topTexture:BitmapData, bottomTexture:BitmapData):CrossEngineObject3D
		{
			var backMaterial:BitmapMaterial = new BitmapMaterial(backTexture);
			var leftMaterial:BitmapMaterial = new BitmapMaterial(leftTexture);
			var frontMaterial:BitmapMaterial = new BitmapMaterial(frontTexture);
			var rightMaterial:BitmapMaterial = new BitmapMaterial(rightTexture);
			var topMaterial:BitmapMaterial = new BitmapMaterial(topTexture);
			var bottomMaterial:BitmapMaterial = new BitmapMaterial(bottomTexture);
			var materialsList:MaterialsList = new MaterialsList({back: backMaterial, left:leftMaterial, front: frontMaterial, right:rightMaterial, top:topMaterial, bottom:bottomMaterial });
			var skybox:Cube = new Cube(materialsList, 512, 512, 512, 4, 4, 4, Cube.ALL);
			skybox.quarterFaces();
			skybox.rotationX = 90;
			skybox.x = skybox.y = 1280
			skybox.scale = 15;
			//return skybox;
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, skybox);
			return crossEngineObject3D;
		}
		
		public function createSprite3D(bitmapData:BitmapData, type:String = ""):CrossEngineObject3D 
		{
			var sprite3D:Sprite3D = new Sprite3D(new MaterialsList([new BitmapMaterial(bitmapData)]), bitmapData.width, bitmapData.height);
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, sprite3D);
			return crossEngineObject3D;	
		}
		
		public function changeTexture( bitmapData:BitmapData, targetObject3D:* ):void  
		{
			var material:MaterialObject3D;
			if (targetObject3D is Sprite3D)
				material = Sprite3D(targetObject3D).displayObject3D.material
			else
				DisplayObject3D(targetObject3D).material
			if (!material)
				return;
			material.bitmap = bitmapData
		}		
		
		//--------------------------------------------------------------------------
		//
		//  Common
		//
		//--------------------------------------------------------------------------	
		
		public function update():void 
		{
			renderer.renderScene(scene, camera, viewport);
		}
		
		public function resize(stageWidth:int, stageHeight:int):void 
		{
			camera.fov = 60;
		}
		
		//--------------------------------------------------------------------------
		//
		//  INTERFACE render3d.interfaces.IRender3DForObject3D
		//
		//--------------------------------------------------------------------------
		
		public function copyTransformation(sourceMatrix3D:Matrix3D, targetObject3D:*):void 
		{
			var components:Vector.<Vector3D> = sourceMatrix3D.decompose();
			DisplayObject3D(targetObject3D).x = components[0].x;
			DisplayObject3D(targetObject3D).y = components[0].y;
			DisplayObject3D(targetObject3D).z = components[0].z;
			
			DisplayObject3D(targetObject3D).rotationX = components[1].x*toDEGREES-90;
			DisplayObject3D(targetObject3D).rotationY = components[1].y*toDEGREES;
			DisplayObject3D(targetObject3D).rotationZ = components[1].z*toDEGREES;			
			
			DisplayObject3D(targetObject3D).scaleX = components[2].x;
			DisplayObject3D(targetObject3D).scaleY = components[2].y;
			DisplayObject3D(targetObject3D).scaleZ = components[2].z;
		}
		
		
		public function copyPosition(sourceMatrix3D:Matrix3D, targetObject3D:*):void 
		{
			var components:Vector.<Vector3D> = sourceMatrix3D.decompose();
			DisplayObject3D(targetObject3D).x = components[0].x;
			DisplayObject3D(targetObject3D).y = components[0].y;
			DisplayObject3D(targetObject3D).z = components[0].z;
		}
		
		//--------------------------------------------------------------------------
		//
		//  INTERFACE render3d.interfaces.abstract.IRender3DAbstract
		//
		//--------------------------------------------------------------------------
		
		public function addChild(child:CrossEngineObject3D):void
		{
			scene.addChild(child.object3D);
		}
		
		public function removeChild(child:CrossEngineObject3D):void
		{
			scene.removeChild(child.object3D);
		}
	}
}